/** @file

  Copyright (C) 2022 - 2023, Phytium Technology Co., Ltd. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Se.h"

#define _MYSTR(a)         #a
#define MYTKN2STR(a)      _MYSTR(a)
#define BUSY              Bus is busy Time out
#define WRITE_REGSITER    Write register fail Time out

STATIC
inline
UINT32
I2cReadReg(
  IN UINTN Reg
  )
{
  UINT32  Value;
  Value = MmioRead32(FT_I2C1_BASE +Reg);
  return Value;
}

STATIC
inline
VOID
I2cWriteReg(
  UINTN Reg, 
  UINT32 Value
  )
{
  UINTN RegAddr ;
  RegAddr = FT_I2C1_BASE + Reg;
  MmioWrite32(RegAddr, Value);
}
UINTN GetTicks(void)
{
  UINTN cntpct;
  asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));

  return cntpct;
}

void UDelay(UINT32 Time)
{
  UINTN Start, End;
  Start = GetTicks();
  End = Start + Time * TICKS_PER_US;
  while(GetTicks() <= End) {
  }
}

#if 0
STATIC
VOID 
my_mdelay(
  VOID
  )
{
  volatile UINT32 i = 0;
  volatile UINT32 j = 0;
  for (i=0; i<5000; i++){
    j +=1;
  }
}


STATIC
VOID 
my_UDelay(
  VOID
  )
{
  volatile UINT32 i = 0;
  volatile UINT32 j = 0;
  for (i=0; i<500; i++){
    j +=1;
  }
}
#endif
STATIC
VOID
FtI2cSeInit(
  VOID
  )
{
  UINT8 i = 0;
  for (i=0; i<28; i++){
    WriteL(0x0e884444, 0x28180200);//sda
    UDelay(1800);
    WriteL(0xee884444, 0x28180200);//I2C1
    UDelay(1800);
  }
  UDelay(1800);
}

STATIC
VOID
FtI2cSeUinit(
  VOID
  )
{
  UINT8 i = 0;
  for (i=0; i<9; i++){
    WriteL(0x0e884444, 0x28180200);//sda
    UDelay(1800);
    WriteL(0xee884444, 0x28180200);//I2C1
    UDelay(1800);
  }
  UDelay(1800);
}

STATIC
VOID
FtI2cCtlInit(
  UINT8 DevAddr
  )
{
  UINT32 hcnt = 166*600/1000 + 1;
  UINT32 lcnt = 166*1300/1000 + 1;
  I2cWriteReg(IC_ENABLE,      0x00);
  I2cWriteReg(IC_FS_SCL_HCNT, hcnt);      //High level duration counter value in standard mode
  I2cWriteReg(IC_FS_SCL_LCNT, lcnt);      //Low level duration counter value in standard mode
  I2cWriteReg(IC_RX_TL,       0xFF);
  I2cWriteReg(IC_TX_TL,       0xFF);
  I2cWriteReg(IC_CON,         0x65);
  I2cWriteReg(IC_SDA_HOLD,    0x31);
  I2cWriteReg(IC_TAR,         DevAddr);
  I2cWriteReg(IC_ENABLE,      0x01);
}

STATIC
EFI_STATUS
FtI2cWaitBusy(
  VOID
  )
{
  UINT8 Timeout;
  Timeout = 32;

  CHAR8                         BiosVersion[] = {MYTKN2STR(BUSY)};
  CHAR8                         Buffer[100];
  UINTN                         CharCount;
  CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"%a \n\r", BiosVersion);
  while((I2cReadReg(IC_STATUS) & IC_STATUS_MA) || !(I2cReadReg(IC_STATUS) & IC_STATUS_TFE))
  {
    /* Evaluate Timeout */
    MicroSecondDelay(1000);
    if (Timeout-- == 0) {
      SerialPortWrite ((UINT8 *) Buffer, CharCount);
      return EFI_TIMEOUT;
    }
  }
  return EFI_SUCCESS;
}

STATIC
UINT32
FtI2cRxfifoFlush(
  VOID
  )
{
  volatile UINT32 Tmp = 0;
  while(I2cReadReg(IC_STATUS) & 0x08) {  //flush the i2c RX FIFO
       Tmp = I2cReadReg(IC_DATA_CMD);
       UDelay(100);
  }
  return Tmp;
}

STATIC
VOID
FtI2cDisable(
  VOID
  )
{
  I2cWriteReg(IC_ENABLE,      0x00);
  I2cWriteReg(IC_FS_SCL_HCNT,0x00);      //标准模式下高电平持续时间计数值
  I2cWriteReg(IC_FS_SCL_LCNT,0x00);      //标准模式下低电平持续时间计数值
  I2cWriteReg(IC_RX_TL,      0x00);
  I2cWriteReg(IC_TX_TL,      0x00);
  I2cWriteReg(IC_CON,       0x00);
  I2cWriteReg(IC_SDA_HOLD,  0x00);
  I2cWriteReg(IC_TAR,0x00);
}



STATIC
VOID
FtI2cSendStop(
  VOID
  )
{
  I2cWriteReg(IC_DATA_CMD, (0x01<<8)); //[7:0], Add a stop signal to send
  I2cWriteReg(IC_DATA_CMD, (0x01<<8)); //[7:0], Add a stop signal to send
  I2cWriteReg(IC_DATA_CMD, (0x01<<8)); //[7:0], Add a stop signal to send
  I2cWriteReg(IC_DATA_CMD, (0x01<<8)); //[7:0], Add a stop signal to send
  I2cWriteReg(IC_DATA_CMD, (0x01<<8) | (0x1<<9));  //[7:0], Add a stop signal to send 
}


STATIC
INT32
FtI2cCheckAbrt(
  UINT8 DevAddr
  )
{
  volatile UINT32 TmpState = 0x00;
  volatile UINT32 TmpState1 = 0x00;
  TmpState = I2cReadReg(IC_TX_ABRT_SOURCE);
  if (TmpState){
    FtI2cDisable();
    DEBUG((EFI_D_INFO,"ABRT:%x\n",I2cReadReg(IC_TX_ABRT_SOURCE)));
    TmpState = I2cReadReg(IC_CLR_INTR);                  //clear all
    TmpState1 = I2cReadReg(IC_CLR_TX_ABRT);              //clear all
    DEBUG((EFI_D_INFO,"clr: %x, %x\n",TmpState, TmpState1));
    FtI2cSeInit();
    UDelay(1800);
    FtI2cCtlInit(DevAddr);
    UDelay(200);
    FtI2cSendStop();
    UDelay(200);
    FtI2cWaitBusy();
    FtI2cRxfifoFlush();
    UDelay(200);
    DEBUG((EFI_D_INFO,"restart: done\n")
);
    return TmpState;
  }
  return 0;
}


STATIC
EFI_STATUS
FtI2cWriteReg(
  UINT8 DevAddr,
  UINT64 RegAddr
  )
{
  //写40bit地址
  UINTN Timeout;
  Timeout = 32;
  CHAR8                         BiosVersion[] = {MYTKN2STR(WRITE_REGSITER)};
  CHAR8                         Buffer[100];
  UINTN                         CharCount;
  CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"%a \n\r", BiosVersion);

  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,      (RegAddr>>32) & 0xff);      //[39:32]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,      (RegAddr>>24) & 0xff);      //[31:24]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,      (RegAddr>>16) & 0xff);      //[23:16]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,      (RegAddr>>8) & 0xff);      //[15:8]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,      (RegAddr>>0) & 0xff);      //[7:0]
  UDelay(200);
  while(1){
    if (((I2cReadReg(IC_STATUS) >> 2) & 0x1 ) == 1){
      break;
    } else {
        MicroSecondDelay(1000);
        if (Timeout -- == 0) {
          SerialPortWrite ((UINT8 *) Buffer, CharCount);
          return EFI_TIMEOUT;
       }
    }
  }
  return 0;
}


STATIC
INT32
FtI2cXferFinish(
  VOID
  )
{
  volatile INT32 Tmp;
  while(1){
    if (I2cReadReg(IC_RAW_INTR_STAT) & 0x200) {     //待[9]=1,stop_det
      Tmp = I2cReadReg(IC_CLR_STOP_DET);                  //读Reg,clear stop det
      break;
    }
  }
  while((I2cReadReg(IC_STATUS) & 0x20) || !(I2cReadReg(IC_STATUS) & 0x04));
  FtI2cRxfifoFlush();
  return Tmp;
}

INT32
FtI2cReadSe(
  UINT32    DevAddr,
  UINT64   RegAddr,
  UINT32   *ReadValue
  )
{
  UINT8 i = 0;

  volatile UINT32 ReadTemp = 0x00;

  // DEBUG((EFI_D_INFO,"read_reg_addr: 0x%llx\n", RegAddr));
  FtI2cDisable();
  FtI2cSeInit();
  UDelay(200);
  FtI2cCtlInit(DevAddr);
  UDelay(200);
  FtI2cSendStop();
  UDelay(200);
  FtI2cWaitBusy();
  FtI2cRxfifoFlush();
  UDelay(200);
  if (FtI2cCheckAbrt(DevAddr)){
    DEBUG((EFI_D_INFO,"1_ABRT:%s,%d\n",__func__,__LINE__));
  }
  
  if(FtI2cWriteReg(DevAddr, RegAddr)){
    if(FtI2cWriteReg(DevAddr, RegAddr)){
      return -1;
    }
  }

  I2cWriteReg(IC_DATA_CMD,      0x1);                   //send d_width, 0 for 4 byte, 1 for 8 byte

  //receive 8 byte
  for (i=0; i<7; i++){
    I2cWriteReg(IC_DATA_CMD,       0x1<<8);
  }
  I2cWriteReg(IC_DATA_CMD,  (0x01 << 8) | (0x01 << 9)); //master noack/stop
  FtI2cWaitBusy();
  if (FtI2cCheckAbrt(DevAddr)){
    DEBUG((EFI_D_INFO,"1_ABRT:%s,%d\n",__func__,__LINE__));
    return -1;
  }

  for(i=0; i<8; i++){
    while(((I2cReadReg(IC_STATUS)>> 3) & 0x1) != 1);      //rx fifo not empty
    ReadTemp = I2cReadReg(IC_DATA_CMD);
    *ReadValue |= (ReadTemp << ((7-i)*8));
  }
  if (FtI2cCheckAbrt(DevAddr)){
    DEBUG((EFI_D_INFO,"2_ABRT:%s,%d\n",__func__,__LINE__));
    return -1;
  }
  FtI2cWaitBusy();
  while(I2cReadReg(IC_STATUS) & 0x20);//等待[5]=0,master idle [2]=1
  FtI2cSeUinit();
  FtI2cXferFinish();
  //p_printf("read Value: 0x%0.2x\n", *ReadValue); 
  return 0;
}

INT32
FtI2cWriteSe(
  UINT32 DevAddr,
  UINT64 RegAddr,
  UINT64 write_value,
  UINT8  flag
  )
{
  //uint8_t i = 0;
  //p_printf("write_reg_addr: 0x%llx\n", RegAddr);

  FtI2cDisable();
  FtI2cSeInit();
  UDelay(200);
  FtI2cCtlInit(DevAddr);
  UDelay(200);
  FtI2cSendStop();
  UDelay(200);
  FtI2cWaitBusy();
  FtI2cRxfifoFlush(); //clear send_stop flush
  UDelay(200);
  if (FtI2cCheckAbrt(DevAddr)){
    DEBUG((EFI_D_INFO,"3_ABRT:%s,%d\n",__func__,__LINE__));
  }
  if(FtI2cWriteReg(DevAddr, RegAddr)){
    if(FtI2cWriteReg(DevAddr, RegAddr)){
      return -1;
    }
  }

  if (FtI2cCheckAbrt(DevAddr)){
    DEBUG((EFI_D_INFO,"4_ABRT:%s,%d\n",__func__,__LINE__));
    return -1;
  }
  //DEBUG((EFI_D_INFO, "%a line %d:\n",__func__,__LINE__));
  if (flag == SE_CMD){
    write_value = write_value | (0x1<<8);
  }

  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,  0x00);  //send d_width, 0 for 4 byte, 1 for 8 byte

  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,          ((write_value>>24)&0xff) | (0x0<<8));           //[31:24]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,          ((write_value>>16)&0xff) | (0x0<<8));           //[23:16]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,          ((write_value>>8)&0xff) | (0x0<<8));              //[15:8]
  while(((I2cReadReg(IC_STATUS)>>1)&0x1) != 1);
  I2cWriteReg(IC_DATA_CMD,          ((write_value>>0)&0xff) | (0x0<<8) | (0x1<<9)); //[7:0] , 增加发送一个stop信号   
  while(((I2cReadReg(IC_STATUS)>>2)&0x1) != 1);
  if (FtI2cCheckAbrt(DevAddr)){
    DEBUG((EFI_D_INFO,"5_ABRT:%s,%d\n",__func__,__LINE__));
    return -1;
  }

  while(I2cReadReg(IC_STATUS) & 0x20);//等待[5]=0,master idle [2]=1
  FtI2cSeUinit();
  FtI2cXferFinish();
  //p_printf("write Value: 0x%0.2llx\n", write_value); 
  return  0;
}


INT32
ReadSeS3Flag(
  VOID
  )
{
  UINT32 ReadTmp = 0;
  if (FtI2cReadSe(SE_DEV_ADDR, SE_REG_S3_FLAG, &ReadTmp)){
    DEBUG((EFI_D_INFO,"read_se_again <ReadSeS3Flag> \n"));
    if(FtI2cReadSe(SE_DEV_ADDR, SE_REG_S3_FLAG, &ReadTmp))
        return -1;
  }
  return ReadTmp;
}

INT32
WriteSeCmd(
  UINT32 Cmd
  )
{
  UINT32 ReadTmp = 0;
  if (FtI2cWriteSe(SE_DEV_ADDR,SE_REG_HAND1,Cmd,SE_CMD)){
    DEBUG((EFI_D_INFO,"write_se_again <WriteSeCmd>\n"));
    FtI2cWriteSe(SE_DEV_ADDR,SE_REG_HAND1,Cmd,SE_CMD);
  }
  MicroSecondDelay(50000);
  if (FtI2cReadSe(SE_DEV_ADDR, SE_REG_CHECK, &ReadTmp)){
    DEBUG((EFI_D_INFO,"read_se_again <WriteSeCmd>\n"));
    FtI2cReadSe(SE_DEV_ADDR, SE_REG_CHECK, &ReadTmp);
  }
  if (ReadTmp != Cmd) {
    DEBUG((EFI_D_ERROR, "ReadTmp is 0x%x\n",ReadTmp));
    DEBUG((EFI_D_ERROR, "Cmd is 0x%x\n",Cmd));
    return -1;
  }
  return 0;
}


UINTN
ReadSeState(
  VOID
  )
{
  volatile UINT32 State = 0, Cnt = 0;
  State = ReadSeS3Flag();
  while (State == -1) {
    Cnt++;
    if(Cnt > 3) {
      DEBUG((EFI_D_ERROR,"read s3_flag error.\n")); 
      return -1;
      }
    State = ReadSeS3Flag();
  }
  DEBUG((EFI_D_INFO,"s3_flag is 0x%x\n",State));
  return State;
}

VOID
SendSeCtr(
  UINT32 Cmd
  )
{
  volatile INT32 State = 0, Cnt = 0;
  UINT8  A[3];
  A[1] = '\n';
  A[2] = '\r';
  State = WriteSeCmd(Cmd);
  A[0] = Cmd + '0';
  while (State) {
    Cnt++;
    if (Cnt > 3) {
      SerialPortWrite ((UINT8 *)A,3);
      DEBUG((EFI_D_INFO,"send Cmd error: %x\n",Cmd));
      break;
    }
  State = WriteSeCmd(Cmd);
  }
}
